// phalanx referenced_vector.hpp.cu header file

// Copyright (c) 2010 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

#ifndef IG_PHALANX_UTILITY_DETAIL_REFERENCED_VECTOR_HPP_CU_ONCE_
#define IG_PHALANX_UTILITY_DETAIL_REFERENCED_VECTOR_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/inc.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/comma.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/comparison/not_equal.hpp>

#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/placeholders.hpp>

#include <phalanx/utility/apply_if.hpp.cu>
#include <phalanx/utility/enable_if.hpp.cu>

#include <phalanx/type_traits/builtin_traits.hpp.cu>
#include <phalanx/type_traits/remove_cv.hpp.cu>
#include <phalanx/type_traits/is_const.hpp.cu>
#include <phalanx/type_traits/is_volatile.hpp.cu>
#include <phalanx/type_traits/is_same.hpp.cu>
#include <phalanx/type_traits/add_const.hpp.cu>
#include <phalanx/type_traits/add_volatile.hpp.cu>

// NOTE: Those classes should not use directly by user.

namespace phalanx
{

namespace refvector_detail
{

template < typename V,
  typename pV  = typename remove_cv< V >::type,
  unsigned int = vector_type_traits< pV >::size_value >
struct ref_vector_impl_;

#define EXPAND_MEMBER_IMPL_( unused_z_, num_, param_ ) \
  BOOST_PP_TUPLE_ELEM( 4, 1, param_ )( \
    BOOST_PP_TUPLE_ELEM( 4, num_, BOOST_PP_TUPLE_ELEM( 4, 3, param_ ) ) ) \
  BOOST_PP_IF( \
    BOOST_PP_NOT_EQUAL( \
      num_, \
      BOOST_PP_TUPLE_ELEM( 4, 0, param_ ) ), \
    BOOST_PP_TUPLE_ELEM( 4, 2, param_ ), \
    BOOST_PP_EMPTY )()

#define EXPAND_MEMBER_( param_, sep_op_, op_ ) \
  BOOST_PP_CAT( BOOST_PP_REPEAT_, BOOST_PP_TUPLE_ELEM( 3, 0, param_ ) )( \
    BOOST_PP_INC( BOOST_PP_TUPLE_ELEM( 3, 1, param_ ) ), \
    EXPAND_MEMBER_IMPL_, \
    ( BOOST_PP_TUPLE_ELEM( 3, 1, param_ ), \
      op_, \
      sep_op_, \
      BOOST_PP_TUPLE_ELEM( 3, 2, param_ ) ) )

#define OP_MEMBER_DECL( val_ ) val_

#define OP_INIT_FROM_VEC( val_ ) val_( v.val_ )

#define OP_CTOR_PARAM( val_ ) \
  typename traits_type::reference BOOST_PP_CAT( val_, _ )

#define OP_INIT_FROM_EACH( val_ ) val_( BOOST_PP_CAT( val_, _ ) )

#define SEMICOLON_() ;
#define OP_ASSIGN( val_ ) val_ = v.val_

#define REF_VECTOR_IMPL( z_, num_, members_ ) \
  template < typename V, typename pV > \
  struct ref_vector_impl_< V, pV, BOOST_PP_INC( num_ ) > \
  { \
  private: \
      typedef typename vector_type_traits< pV >::value_type pvalue_type_; \
  \
  public: \
      typedef base_traits< \
        typename lazy_apply_if< \
          is_const< V >, \
          add_const< ::boost::mpl::_1 >, \
          apply_if< \
            is_volatile< V >, \
            add_volatile< ::boost::mpl::_1 >, \
            pvalue_type_ > >::type > traits_type; \
  \
      typename traits_type::reference \
        EXPAND_MEMBER_( ( z_, num_, members_ ), BOOST_PP_COMMA, OP_MEMBER_DECL ); \
  \
      PHALANX_CONFIG_DEFAULT_FUNCTION( false ) \
      ref_vector_impl_( V &v ) \
        : EXPAND_MEMBER_( ( z_, num_, members_ ), BOOST_PP_COMMA, OP_INIT_FROM_VEC ) {} \
  \
      PHALANX_CONFIG_DEFAULT_FUNCTION( false ) \
      ref_vector_impl_( \
        EXPAND_MEMBER_( ( z_, num_, members_ ), BOOST_PP_COMMA, OP_CTOR_PARAM ) ) \
        : EXPAND_MEMBER_( ( z_, num_, members_ ), BOOST_PP_COMMA, OP_INIT_FROM_EACH ) {} \
  \
      template < typename T > \
      PHALANX_CONFIG_DEFAULT_FUNCTION( false ) \
      typename disable_if< \
        ::boost::mpl::or_< \
          ::boost::mpl::not_< is_same< typename remove_const< V >::type, T > >, \
          is_const< V > >, \
        ref_vector_impl_ & >::type \
      operator=( const T &v ) \
      { \
          EXPAND_MEMBER_( ( z_, num_, members_ ), SEMICOLON_, OP_ASSIGN ); \
          return *this; \
      } \
  \
      PHALANX_CONFIG_DEFAULT_FUNCTION( false ) \
      operator V( void ) const \
      { return V( EXPAND_MEMBER_( ( z_, num_, members_ ), BOOST_PP_COMMA, OP_MEMBER_DECL )  ); } \
  };

BOOST_PP_REPEAT( 4, REF_VECTOR_IMPL, ( x, y, z, w ) );

#undef REF_VECTOR_IMPL
#undef OP_ASSIGN
#undef SEMICOLON_
#undef OP_INIT_FROM_EACH
#undef OP_CTOR_PARAM
#undef OP_INIT_FROM_VEC
#undef OP_MEMBER_DECL
#undef EXPAND_MEMBER_
#undef EXPAND_MEMBER_IMPL_

} // namespace refvector_detail

} // namespace phalanx

#endif // IG_PHALANX_UTILITY_DETAIL_REFERENCED_VECTOR_HPP_CU_ONCE_

